package com.sbc.security.tooltiks;

import com.alibaba.fastjson2.JSON;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.RequiredArgsConstructor;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;

/**
 * @description: JWT令牌提供者类，用于生成和解析JWT令牌。
 * @author: songbaicheng
 * @create: 2024-04-30 15:04
 **/
@Component
@RequiredArgsConstructor
@ConfigurationProperties(prefix = JwtTokenProvider.PREFIX)
public class JwtTokenProvider {

    /**
     * JWT 通用配置前缀
     */
    public static final String PREFIX = "framework.auth.jwt";
    /**
     * 过期时间
     */
    private long expiration;
    /**
     * Token 前缀
     */
    private String tokenPrefix;
    /**
     * 签发者
     */
    private String iss;
    /**
     * 密钥
     */
    private String secret;

    /**
     * 生成JWT令牌。
     *
     * @param username 用户名
     * @return 生成的JWT令牌字符串
     */
    public String generateToken(String username) {
        // 载荷（Payload）部分
        Map<String, Object> claims = new HashMap<>();

        // 用户身份标识
        Map<String, Object> customerUserMap = new HashMap<>();
        customerUserMap.put("username", username);

        return createToken(claims, JSON.toJSONString(customerUserMap));
    }


    /**
     * 根据声明和主题创建JWT令牌。
     *
     * @param claims  JWT的声明部分
     * @param subject 令牌的主题，通常是用户名
     * @return 完整的JWT令牌字符串
     */
    private String createToken(Map<String, Object> claims, String subject) {
        return tokenPrefix + Jwts.builder()
                .setClaims(claims)
                .setSubject(subject)
                .setIssuedAt(new Date())
                .setIssuer(iss)
                .setExpiration(new Date(System.currentTimeMillis() + expiration * 1000))
                .signWith(SignatureAlgorithm.HS256, secret)
                .compact();
    }

    /**
     * 验证JWT令牌的有效性。
     *
     * @param token 待验证的JWT令牌
     * @return 是否有效
     */
    public boolean validateToken(String token) {
        return isTokenExpired(token);
    }

    /**
     * 从JWT令牌中提取用户名。
     *
     * @param token JWT令牌字符串
     * @return 提取的用户名
     */
    public String extractUsername(String token) {
        return extractClaim(token, Claims::getSubject);
    }

    /**
     * 从JWT令牌中提取过期时间。
     *
     * @param token JWT令牌字符串
     * @return 过期时间
     */
    private Date extractExpiration(String token) {
        return extractClaim(token, Claims::getExpiration);
    }


    /**
     * 从JWT令牌中提取特定声明。
     *
     * @param token          JWT令牌字符串
     * @param claimsResolver 提取声明的函数
     * @param <T>            声明的类型
     * @return 提取的声明值
     */
    private <T> T extractClaim(String token, Function<Claims, T> claimsResolver) {
        final Claims claims = extractAllClaims(token);
        return claimsResolver.apply(claims);
    }

    /**
     * 解析JWT令牌中的所有声明。
     *
     * @param token JWT令牌字符串
     * @return 声明对象
     */
    private Claims extractAllClaims(String token) {
        return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
    }

    /**
     * 判断JWT令牌是否已过期。
     *
     * @param token JWT令牌字符串
     * @return 是否过期
     */
    private Boolean isTokenExpired(String token) {
        return extractExpiration(token).before(new Date());
    }
}
